{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Stairs Circuit and its gradients evaluated using native simulators\n",
    "\n",
    "A \"quantum cost function\" is the mean value of a\n",
    "Hermitian operator, wherein that mean value is calculated empirically from\n",
    "the data yielded by a physical quantum computer. (In this notebook, however, we fake that data\n",
    "with a Qubiter simulator. Just warming up. Brrmm! Brrmm! Everything is setup\n",
    "so that calls to a real physical qc such as Rigetti's qc can be easily substituted for the \n",
    "calls to the Qubiter simulator)\n",
    "\n",
    "A Stairs circuit is a quantum circuit that in its full generality can \n",
    "parametrize a completely general quantum state vector. \n",
    "\n",
    "In this notebook, we consider a special quantum cost function in which\n",
    "a Stairs circuit provides the state vector with which the mean values are calculated.\n",
    "I like to say that the Stairs circuit is the kernel of the quantum cost function. Our ultimate intention is to\n",
    "minimize this cost function using gradient descent. But to do that, we must first calculate\n",
    "the derivatives (gradients) of the cost function. That is what we will do in this notebook: calculate all the derivatives of a quantum cost function whose kernel is a Stairs circuit.\n",
    "\n",
    " In particular,\n",
    "this notebook runs through their paces 4 Qubiter classes that increasingly build on each other\n",
    "\n",
    "*  `StairsCkt_writer`, writes English and Picture files for a Stairs quantum circuit\n",
    "*  `StairsDeriv_writer`, writes English and Picture files for several quantum circuits that are needed to evaluate the derivatives of a Stairs circuit\n",
    "*  `StairsDeriv_native`, evaluates the 4 derivatives of a single gate of a Stairs circuit. It uses native Qubiter simulators to do this. Qubiter also has an analogous class `StairsDeriv_rigetti` that uses Rigetti simulators or their real physical qc. We will demo that class in a future notebook.\n",
    "*  `StairsAllDeriv_native`, evaluates all the derivatives, for all the gates of a Stairs circuit. It uses native Qubiter simulators to do this. Qubiter also has an analogous class `StairsAllDeriv_rigetti` that uses Rigetti simulators or their real physical qc.  We will demo that class in a future notebook.\n",
    "\n",
    "For an explanation of the theory behind the software that is being demo-ed in this notebook, see the following\n",
    "pdf included with the Qubiter distribution.\n",
    "\n",
    ">Title:\n",
    "Calculation of the Gradient of a Quantum Cost Function using \"Threading\".\n",
    "Application of these \"threaded gradients\" to a\n",
    "Quantum Neural Net\n",
    "inspired by\n",
    "Quantum Bayesian Networks, \n",
    "\n",
    "> https://github.com/artiste-qb-net/qubiter/blob/master/adv_applications/threaded_grad.pdf"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "First change your working directory to the Qubiter directory in your computer, and add its path to the path environment variable."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "/home/rrtucci/PycharmProjects/qubiter/qubiter/jupyter_notebooks\n",
      "/home/rrtucci/PycharmProjects/qubiter\n"
     ]
    }
   ],
   "source": [
    "import os\n",
    "import sys\n",
    "print(os.getcwd())\n",
    "os.chdir('../../')\n",
    "print(os.getcwd())\n",
    "sys.path.insert(0,os.getcwd())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# class StairsCkt_writer"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "loaded OneQubitGate, WITHOUT autograd.numpy\n"
     ]
    }
   ],
   "source": [
    "from qubiter.adv_applications.StairsCkt_writer import *"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "    This class is a subclass of class SEO_writer and it writes a \"Stairs\n",
      "    Circuit\". For example, this is what the Picture file of a Stairs Circuit \n",
      "    looks like for num_qbits = 3 \n",
      "    \n",
      "    U   |   |\n",
      "    O---U   |  \n",
      "    @---U   |   \n",
      "    O---O---U   \n",
      "    O---@---U   \n",
      "    @---O---U  \n",
      "    @---@---U \n",
      "       \n",
      "    Here, U is a general U(2) matrix with 4 parameters, all of which can be \n",
      "    made into placeholder variables. If each U is represented by a node and \n",
      "    the controls of each U represent its parents, then this quantum circuit \n",
      "    can be represented by a fully connected Quantum Bayesian Network (QB \n",
      "    net). (See my >10 year old blog called \"Quantum Bayesian Networks\" for \n",
      "    more info than you would ever want to know about QB nets). \n",
      "    \n",
      "    This class can also be asked to construct a QB net that is **not** fully \n",
      "    connected, by limiting the number of controls for a given U to fewer \n",
      "    than all the ones to its left. For example, suppose that in the \n",
      "    num_qbits=3 case, we restrict the parents of the U in the last step to \n",
      "    just one, instead of the 2 parents that it has in the fully connected \n",
      "    case. Then we get \n",
      "\n",
      "    \n",
      "    U   |   |\n",
      "    O---U   |  \n",
      "    @---U   |   \n",
      "    O---+---U   \n",
      "    @---+---U  \n",
      "       \n",
      "    or \n",
      "    \n",
      "    U   |   |\n",
      "    O---U   |  \n",
      "    @---U   |   \n",
      "    |   O---U   \n",
      "    |   @---U     \n",
      "    \n",
      "    The constructor of this class has as input an ordered dictionary called \n",
      "    gate_str_to_rads_list. This dictionary gives for each gate in the \n",
      "    quantum circuit, a gate string gate_str that specifies the gate. \n",
      "    gate_str_to_rads_list maps gate_str to a list of 4 floats (or \n",
      "    placeholder variables for those floats) for the 4 parameters of the U \n",
      "    matrix. For example, here are possible values for gate_str_to_rads_list \n",
      "    for the num_qbits=3 fully connected qb net \n",
      "    \n",
      "    with every rads_list item filled with the same constant .3\n",
      "    {'prior': [0.3, 0.3, 0.3, 0.3],\n",
      "     '2F': [0.3, 0.3, 0.3, 0.3],\n",
      "     '2T': [0.3, 0.3, 0.3, 0.3],\n",
      "     '2F1F': [0.3, 0.3, 0.3, 0.3],\n",
      "     '2F1T': [0.3, 0.3, 0.3, 0.3],\n",
      "     '2T1F': [0.3, 0.3, 0.3, 0.3],\n",
      "     '2T1T': [0.3, 0.3, 0.3, 0.3]}\n",
      "     \n",
      "    with every rads_list item filled by a random number between 0 and 2pi\n",
      "    {'prior': [0.46731839721496604,\n",
      "               0.012285135138256131,\n",
      "               0.20001353832948487,\n",
      "               0.36694428209569985],\n",
      "     '2F': [4.1968011007222898,\n",
      "            5.1978252498063808,\n",
      "            4.8063090848060321,\n",
      "            4.2509081392354409],\n",
      "     '2T': [4.3359074640905213,\n",
      "            2.0749617893052315,\n",
      "            4.555666727197961,\n",
      "            5.3092010293653802],\n",
      "     '2F1F': [0.99177045463186475,\n",
      "              3.3344615340103325,\n",
      "              2.1441702948866386,\n",
      "              2.4603764283165521],\n",
      "     '2F1T': [4.0909522483111145,\n",
      "              2.0714182784661888,\n",
      "              5.4034187072431923,\n",
      "              6.0856723571386766],\n",
      "     '2T1F': [4.0000452017061194,\n",
      "              3.7193341571216658,\n",
      "              3.381322125034953,\n",
      "              5.4492142181489802],\n",
      "     '2T1T': [6.2597553541046853,\n",
      "              0.077807529496169509,\n",
      "              3.7389318319862217,\n",
      "              6.2233264819972307]}\n",
      "              \n",
      "    with every rads_list item filled by a unique placeholder variable string\n",
      "    {'prior': ['#50', '#51', '#52', '#53'],\n",
      "     '2F': ['#500', '#501', '#502', '#503'],\n",
      "     '2T': ['#510', '#511', '#512', '#513'],\n",
      "     '2F1F': ['#5000', '#5001', '#5002', '#5003'],\n",
      "     '2F1T': ['#5010', '#5011', '#5012', '#5013'],\n",
      "     '2T1F': ['#5100', '#5101', '#5102', '#5103'],\n",
      "     '2T1T': ['#5110', '#5111', '#5112', '#5113']}\n",
      "\n",
      "    This is what gate_str_to_rads_list looks like in the num_qbits=3 case, \n",
      "    when the last U has only one parent (qbit 2) instead of two parents (\n",
      "    qbits 1 and 2): \n",
      "    \n",
      "    {'prior': ['#50', '#51', '#52', '#53'],\n",
      "     '2F': ['#500', '#501', '#502', '#503'],\n",
      "     '2T': ['#510', '#511', '#512', '#513'],\n",
      "     '2F1_': ['#5050', '#5051', '#5052', '#5053'],\n",
      "     '2T1_': ['#5150', '#5151', '#5152', '#5153']}\n",
      "     \n",
      "    Note that all placeholder strings begin with '#5' to insure that once\n",
      "    the hash character is removed, the remaining number doesn't start with\n",
      "    '0'. Note that characters '_' and '5' represent bits whose values are\n",
      "    unspecified.\n",
      "\n",
      "    Attributes\n",
      "    ----------\n",
      "    gate_str_to_rads_list : OrderedDict[str, list[float|str]]\n",
      "\n",
      "    \n"
     ]
    }
   ],
   "source": [
    "# print docstring of the class\n",
    "print(StairsCkt_writer.__doc__)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Before using class `StairsCkt_writer` to write a quantum circuit, \n",
    "we introduce some static methods belonging to this class\n",
    "that are very helpful, not just with \n",
    "this class, but with all the other classes related to the stairs circuit.\n",
    "\n",
    "`get_gate_str_to_rads_list()` returns a dictionary that maps a gate string to\n",
    "a list of 4 radians. The gate string is the unique name we will give to each gate of \n",
    "the Stairs circuit, and the 4 radians are the values of the angles\n",
    "characterizing a general U(2) transformation, $(t_0, t_1, t_2, t_3)$\n",
    "in $e^{i(t_0, + t_1\\sigma_X + t_2\\sigma_Y+ t_3\\sigma_Z)}$,\n",
    "where $\\sigma_X, \\sigma_Y, \\sigma_Z$ are the Pauli matrices.\n",
    "The user can select among 3 fill types, either 'const' for a constant value,\n",
    "'rand' for random values, or '#int' for a hash followed by a unique int. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "OrderedDict([('prior', [0.3, 0.3, 0.3, 0.3]),\n",
      "             ('2F', [0.3, 0.3, 0.3, 0.3]),\n",
      "             ('2T', [0.3, 0.3, 0.3, 0.3]),\n",
      "             ('2F1F', [0.3, 0.3, 0.3, 0.3]),\n",
      "             ('2F1T', [0.3, 0.3, 0.3, 0.3]),\n",
      "             ('2T1F', [0.3, 0.3, 0.3, 0.3]),\n",
      "             ('2T1T', [0.3, 0.3, 0.3, 0.3])])\n",
      "OrderedDict([('prior',\n",
      "              [0.3731542025285749,\n",
      "               0.8682261236993244,\n",
      "               0.4098652664932987,\n",
      "               0.18735731532561795]),\n",
      "             ('2F',\n",
      "              [5.371921981411959,\n",
      "               4.922695285300463,\n",
      "               1.2485666086608223,\n",
      "               0.24257745941669465]),\n",
      "             ('2T',\n",
      "              [3.822891578429376,\n",
      "               4.97195340729012,\n",
      "               4.558138553696292,\n",
      "               1.0280497429106734]),\n",
      "             ('2F1F',\n",
      "              [4.31713490932788,\n",
      "               3.094118464780498,\n",
      "               0.9737399907025676,\n",
      "               0.9334937040849988]),\n",
      "             ('2F1T',\n",
      "              [2.180330096430294,\n",
      "               3.389786926987428,\n",
      "               4.0467423880791715,\n",
      "               3.370095598522802]),\n",
      "             ('2T1F',\n",
      "              [2.558702747500637,\n",
      "               1.8959781670397253,\n",
      "               5.026146283583697,\n",
      "               0.09514512424355208]),\n",
      "             ('2T1T',\n",
      "              [4.404715054981431,\n",
      "               1.1885186137925632,\n",
      "               5.7845578291584445,\n",
      "               1.176567854555499])])\n",
      "OrderedDict([('prior', ['#50', '#51', '#52', '#53']),\n",
      "             ('2F', ['#500', '#501', '#502', '#503']),\n",
      "             ('2T', ['#510', '#511', '#512', '#513']),\n",
      "             ('2F1F', ['#5000', '#5001', '#5002', '#5003']),\n",
      "             ('2F1T', ['#5010', '#5011', '#5012', '#5013']),\n",
      "             ('2T1F', ['#5100', '#5101', '#5102', '#5103']),\n",
      "             ('2T1T', ['#5110', '#5111', '#5112', '#5113'])])\n"
     ]
    }
   ],
   "source": [
    "num_qbits = 3\n",
    "for fill_type in ['const', 'rand', '#int']:\n",
    "    di = StairsCkt_writer.get_gate_str_to_rads_list(\n",
    "        num_qbits, fill_type, rads_const=.3)\n",
    "    pp.pprint(di)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Above, we asked `get_gate_str_to_rads_list()`\n",
    "to print a dictionary  for a fully connected Quantum Bayesian network,\n",
    "one in which every gate has all qubits to the left of its U as parents. It is \n",
    "also possible to ask `get_gate_str_to_rads_list()`\n",
    "to print a dictionary for a non-fully connected QB net,\n",
    "wherein only some, not all, of the qubits to the left of the U are parents.\n",
    "This can be done by specifying a value for \n",
    "\n",
    "`u2_bit_to_higher_bits`\n",
    "\n",
    "For a 3 qubit case, if one sets that variable to\n",
    "\n",
    "`{0: [1, 2], 1: [2], 2: []}` \n",
    "\n",
    "or to None, one gets a fully connected QB net.\n",
    "This is what happens if one sets it to \n",
    "\n",
    "`{0: [2], 1: [2], 2: []}`\n",
    "\n",
    "instead:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "OrderedDict([('prior', ['#50', '#51', '#52', '#53']),\n",
      "             ('2F', ['#500', '#501', '#502', '#503']),\n",
      "             ('2T', ['#510', '#511', '#512', '#513']),\n",
      "             ('2F1_', ['#5050', '#5051', '#5052', '#5053']),\n",
      "             ('2T1_', ['#5150', '#5151', '#5152', '#5153'])])\n"
     ]
    }
   ],
   "source": [
    "u2_bit_to_higher_bits = {0: [2], 1: [2], 2: []}\n",
    "di = StairsCkt_writer.get_gate_str_to_rads_list(\n",
    "        num_qbits, \"#int\", u2_bit_to_higher_bits=u2_bit_to_higher_bits)\n",
    "pp.pprint(di)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Another helpful static method of the class `StairsCkt_writer`  is `get_var_num_to_rads()`.\n",
    "If the input `di` equals a `gate_str_to_rads_list` dictionary,\n",
    "then this method extracts all the ints to the right of a hash\n",
    "and maps them to a float. The float can be either a constant (fill_type='const')\n",
    "or a random number (fill_type='rand')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{50: 0.3,\n",
      " 51: 0.3,\n",
      " 52: 0.3,\n",
      " 53: 0.3,\n",
      " 500: 0.3,\n",
      " 501: 0.3,\n",
      " 502: 0.3,\n",
      " 503: 0.3,\n",
      " 510: 0.3,\n",
      " 511: 0.3,\n",
      " 512: 0.3,\n",
      " 513: 0.3,\n",
      " 5050: 0.3,\n",
      " 5051: 0.3,\n",
      " 5052: 0.3,\n",
      " 5053: 0.3,\n",
      " 5150: 0.3,\n",
      " 5151: 0.3,\n",
      " 5152: 0.3,\n",
      " 5153: 0.3}\n"
     ]
    }
   ],
   "source": [
    "vn_to_r = StairsCkt_writer.get_var_num_to_rads(di,\n",
    "                                               fill_type='const',\n",
    "                                               rads_const=.3)\n",
    "pp.pprint(vn_to_r)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Another helpful static method of the class `StairsCkt_writer`  is `make_array_from_gate_str_to_rads_list()`.\n",
    "If the input `di` equals a `gate_str_to_rads_list` dictionary,\n",
    "then this method converts that dictionary into a numpy array. (It works even for strings!)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "arr=\n",
      " [['#50' '#51' '#52' '#53']\n",
      " ['#500' '#501' '#502' '#503']\n",
      " ['#510' '#511' '#512' '#513']\n",
      " ['#5050' '#5051' '#5052' '#5053']\n",
      " ['#5150' '#5151' '#5152' '#5153']]\n"
     ]
    }
   ],
   "source": [
    "arr = StairsCkt_writer.make_array_from_gate_str_to_rads_list(di)\n",
    "print(\"arr=\\n\", arr)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "After much ado, we finally call the constructor of class  `StairsCkt_writer`.\n",
    "This writes English and Picture files in the `io_folder`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "num_qbits = 4\n",
    "gate_str_to_rads_list = StairsCkt_writer.get_gate_str_to_rads_list(\n",
    "    num_qbits, '#int')\n",
    "file_prefix = 'stairs_writer_test'\n",
    "emb = CktEmbedder(num_qbits, num_qbits)\n",
    "wr = StairsCkt_writer(gate_str_to_rads_list, file_prefix, emb)\n",
    "wr.close_files()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Next, we ask the writer `wr` to print the English and Picture files that it just created"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<table style='font-family:monospace'><tr><td style='border-right:1px solid red;'>1</td><td style='text-align:left;'><pre>U_2_\t#50\t#51\t#52\t#53\tAT\t3</pre></td></tr><td style='border-right:1px solid red;'>2</td><td style='text-align:left;'><pre>U_2_\t#500\t#501\t#502\t#503\tAT\t2\tIF\t3F</pre></td></tr><td style='border-right:1px solid red;'>3</td><td style='text-align:left;'><pre>U_2_\t#510\t#511\t#512\t#513\tAT\t2\tIF\t3T</pre></td></tr><td style='border-right:1px solid red;'>4</td><td style='text-align:left;'><pre>U_2_\t#5000\t#5001\t#5002\t#5003\tAT\t1\tIF\t3F\t2F</pre></td></tr><td style='border-right:1px solid red;'>5</td><td style='text-align:left;'><pre>U_2_\t#5010\t#5011\t#5012\t#5013\tAT\t1\tIF\t3F\t2T</pre></td></tr><td style='border-right:1px solid red;'>6</td><td style='text-align:left;'><pre>U_2_\t#5100\t#5101\t#5102\t#5103\tAT\t1\tIF\t3T\t2F</pre></td></tr><td style='border-right:1px solid red;'>7</td><td style='text-align:left;'><pre>U_2_\t#5110\t#5111\t#5112\t#5113\tAT\t1\tIF\t3T\t2T</pre></td></tr><td style='border-right:1px solid red;'>8</td><td style='text-align:left;'><pre>U_2_\t#50000\t#50001\t#50002\t#50003\tAT\t0\tIF\t3F\t2F\t1F</pre></td></tr><td style='border-right:1px solid red;'>9</td><td style='text-align:left;'><pre>U_2_\t#50010\t#50011\t#50012\t#50013\tAT\t0\tIF\t3F\t2F\t1T</pre></td></tr><td style='border-right:1px solid red;'>10</td><td style='text-align:left;'><pre>U_2_\t#50100\t#50101\t#50102\t#50103\tAT\t0\tIF\t3F\t2T\t1F</pre></td></tr><td style='border-right:1px solid red;'>11</td><td style='text-align:left;'><pre>U_2_\t#50110\t#50111\t#50112\t#50113\tAT\t0\tIF\t3F\t2T\t1T</pre></td></tr><td style='border-right:1px solid red;'>12</td><td style='text-align:left;'><pre>U_2_\t#51000\t#51001\t#51002\t#51003\tAT\t0\tIF\t3T\t2F\t1F</pre></td></tr><td style='border-right:1px solid red;'>13</td><td style='text-align:left;'><pre>U_2_\t#51010\t#51011\t#51012\t#51013\tAT\t0\tIF\t3T\t2F\t1T</pre></td></tr><td style='border-right:1px solid red;'>14</td><td style='text-align:left;'><pre>U_2_\t#51100\t#51101\t#51102\t#51103\tAT\t0\tIF\t3T\t2T\t1F</pre></td></tr><td style='border-right:1px solid red;'>15</td><td style='text-align:left;'><pre>U_2_\t#51110\t#51111\t#51112\t#51113\tAT\t0\tIF\t3T\t2T\t1T</pre></td></tr></table>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<table style='font-family:monospace'><tr><td style='border-right:1px solid red;'>1</td><td style='text-align:left;'><pre>U   |   |   |</pre></td></tr><td style='border-right:1px solid red;'>2</td><td style='text-align:left;'><pre>O---U   |   |</pre></td></tr><td style='border-right:1px solid red;'>3</td><td style='text-align:left;'><pre>@---U   |   |</pre></td></tr><td style='border-right:1px solid red;'>4</td><td style='text-align:left;'><pre>O---O---U   |</pre></td></tr><td style='border-right:1px solid red;'>5</td><td style='text-align:left;'><pre>O---@---U   |</pre></td></tr><td style='border-right:1px solid red;'>6</td><td style='text-align:left;'><pre>@---O---U   |</pre></td></tr><td style='border-right:1px solid red;'>7</td><td style='text-align:left;'><pre>@---@---U   |</pre></td></tr><td style='border-right:1px solid red;'>8</td><td style='text-align:left;'><pre>O---O---O---U</pre></td></tr><td style='border-right:1px solid red;'>9</td><td style='text-align:left;'><pre>O---O---@---U</pre></td></tr><td style='border-right:1px solid red;'>10</td><td style='text-align:left;'><pre>O---@---O---U</pre></td></tr><td style='border-right:1px solid red;'>11</td><td style='text-align:left;'><pre>O---@---@---U</pre></td></tr><td style='border-right:1px solid red;'>12</td><td style='text-align:left;'><pre>@---O---O---U</pre></td></tr><td style='border-right:1px solid red;'>13</td><td style='text-align:left;'><pre>@---O---@---U</pre></td></tr><td style='border-right:1px solid red;'>14</td><td style='text-align:left;'><pre>@---@---O---U</pre></td></tr><td style='border-right:1px solid red;'>15</td><td style='text-align:left;'><pre>@---@---@---U</pre></td></tr></table>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "wr.print_eng_file(jup=True)\n",
    "wr.print_pic_file(jup=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# class StairsDerivCkt_writer "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "from qubiter.adv_applications.StairsDerivCkt_writer import *"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "    This class is a subclass of `SEO_writer`. It writes several intermediary\n",
      "    stairs derivative circuits that will be used in class\n",
      "    `StairsDeriv_native` for calculating the gradients of a quantum cost\n",
      "    function (mean hamiltonian).\n",
      "\n",
      "    Suppose U = exp[i*(t_0 + t_1*sigx + t_2*sigy + t_3*sigz)], where sigx,\n",
      "    sigy, sigz are the Pauli matrices and t_r for r in range(4) are 4 real\n",
      "    parameters. To take the derivative wrt t_r of a given multi-controlled\n",
      "    gate U in a stairs circuit, we need to evaluate several circuits (we\n",
      "    call them dparts, which stands for derivative parts). Say, for instance,\n",
      "    that GATE= @---O---+---U. To calculate d/dt_r GATE(t_0, t_1, t_2, t_3),\n",
      "    for r=0,1, 2, 3, we need to calculate a new circuit wherein the GATE in\n",
      "    the parent circuit is replaced by\n",
      "\n",
      "    sum_k  c_k  @---@---O---+---U_k\n",
      "\n",
      "    (which is said to have `has_neg_polarity`=False) and\n",
      "\n",
      "    sum_k  c_k  @---@---O---+---U_k\n",
      "                Z---@---O   |   |\n",
      "\n",
      "    (which is said to have `has_neg_polarity`=True)\n",
      "\n",
      "    Also, some extra stuff (a coda) must be appended to the end of the\n",
      "    parent stairs circuit.\n",
      "\n",
      "    Note that an extra \"ancilla\" qbit has been added (as the new last qubit)\n",
      "    to the parent stairs circuit being differentiated. So if the parent\n",
      "    stairs circuit has a number `parent_num_qbits` of qubits, then the one\n",
      "    written by this class has that many qubits plus one.\n",
      "\n",
      "    The index r which is in range(4) is called the derivative direction (\n",
      "    `deriv_direc`)\n",
      "\n",
      "    `gate_str_to_rads_list` is the same as for the parent stairs circuit.\n",
      "\n",
      "    `deriv_gate_str` is a well formed gate_str that specifies which U is\n",
      "    being differentiated\n",
      "\n",
      "    The index k is given as a string called `dpart_name` (\"dpart\" stands for\n",
      "    derivative part).\n",
      "\n",
      "    The coefficients c_k can be obtained via the method get_coef_of_dpart()\n",
      "\n",
      "    Each U_k is a U(2) matrix itself, and its 4 parameters are defined in\n",
      "    terms of the parameters tlist=[t_0, t_1, t_2, t_3] of the U(tlist) being\n",
      "    differentiated, via 4 functions of tlist. These functions can be\n",
      "    obtained via the method get_fun_name_to_fun().\n",
      "\n",
      "    Attributes\n",
      "    ----------\n",
      "    deriv_direc : int\n",
      "        in range(4)\n",
      "    deriv_gate_str : str\n",
      "    dpart_name : str\n",
      "    gate_str_to_rads_list : dict[str, list[float]]\n",
      "    has_neg_polarity : bool\n",
      "\n",
      "\n",
      "    \n"
     ]
    }
   ],
   "source": [
    "# print docstring of the class\n",
    "print(StairsDerivCkt_writer.__doc__)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "num_qbits = 4\n",
    "parent_num_qbits = num_qbits - 1  # one bit for ancilla\n",
    "gate_str_to_rads_list = StairsCkt_writer.\\\n",
    "get_gate_str_to_rads_list(\n",
    "    parent_num_qbits, 'const', rads_const=np.pi/2)\n",
    "file_prefix = 'stairs_deriv_writer_test'\n",
    "emb = CktEmbedder(num_qbits, num_qbits)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "One of the inputs to the constructor of class `StairsDerivCkt_writer`\n",
    "is `deriv_gate_str`, which should be a well-formed gate string specifying\n",
    "what gate of the stairs circuit we want to differentiate.\n",
    "For that, we will use the second key of the `gate_str_to_rads_list` dictionary"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2T\n"
     ]
    }
   ],
   "source": [
    "deriv_gate_str = list(gate_str_to_rads_list.keys())[2]\n",
    "print(deriv_gate_str)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We next call the constructor of class\n",
    "`StairsDerivCkt_writer` for two typical cases, \n",
    "and then print the English and Picutre files for each of the 2 cases"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "%%%%%%%%%%%%%%%%%%%%%%%%%%\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<table style='font-family:monospace'><tr><td style='border-right:1px solid red;'>1</td><td style='text-align:left;'><pre>U_2_\t90.000000\t90.000000\t90.000000\t90.000000\tAT\t2</pre></td></tr><td style='border-right:1px solid red;'>2</td><td style='text-align:left;'><pre>U_2_\t90.000000\t90.000000\t90.000000\t90.000000\tAT\t1\tIF\t2F</pre></td></tr><td style='border-right:1px solid red;'>3</td><td style='text-align:left;'><pre>HAD2\tAT\t3</pre></td></tr><td style='border-right:1px solid red;'>4</td><td style='text-align:left;'><pre>U_2_\t180.000000\t90.000000\t90.000000\t90.000000\tAT\t1\tIF\t3T\t2T</pre></td></tr><td style='border-right:1px solid red;'>5</td><td style='text-align:left;'><pre>U_2_\t90.000000\t90.000000\t90.000000\t90.000000\tAT\t0\tIF\t2F\t1F</pre></td></tr><td style='border-right:1px solid red;'>6</td><td style='text-align:left;'><pre>U_2_\t90.000000\t90.000000\t90.000000\t90.000000\tAT\t0\tIF\t2F\t1T</pre></td></tr><td style='border-right:1px solid red;'>7</td><td style='text-align:left;'><pre>U_2_\t90.000000\t90.000000\t90.000000\t90.000000\tAT\t0\tIF\t2T\t1F</pre></td></tr><td style='border-right:1px solid red;'>8</td><td style='text-align:left;'><pre>U_2_\t90.000000\t90.000000\t90.000000\t90.000000\tAT\t0\tIF\t2T\t1T</pre></td></tr></table>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<table style='font-family:monospace'><tr><td style='border-right:1px solid red;'>1</td><td style='text-align:left;'><pre>|   U   |   |</pre></td></tr><td style='border-right:1px solid red;'>2</td><td style='text-align:left;'><pre>|   O---U   |</pre></td></tr><td style='border-right:1px solid red;'>3</td><td style='text-align:left;'><pre>H   |   |   |</pre></td></tr><td style='border-right:1px solid red;'>4</td><td style='text-align:left;'><pre>@---@---U   |</pre></td></tr><td style='border-right:1px solid red;'>5</td><td style='text-align:left;'><pre>|   O---O---U</pre></td></tr><td style='border-right:1px solid red;'>6</td><td style='text-align:left;'><pre>|   O---@---U</pre></td></tr><td style='border-right:1px solid red;'>7</td><td style='text-align:left;'><pre>|   @---O---U</pre></td></tr><td style='border-right:1px solid red;'>8</td><td style='text-align:left;'><pre>|   @---@---U</pre></td></tr></table>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "%%%%%%%%%%%%%%%%%%%%%%%%%%\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<table style='font-family:monospace'><tr><td style='border-right:1px solid red;'>1</td><td style='text-align:left;'><pre>U_2_\t90.000000\t90.000000\t90.000000\t90.000000\tAT\t2</pre></td></tr><td style='border-right:1px solid red;'>2</td><td style='text-align:left;'><pre>U_2_\t90.000000\t90.000000\t90.000000\t90.000000\tAT\t1\tIF\t2F</pre></td></tr><td style='border-right:1px solid red;'>3</td><td style='text-align:left;'><pre>HAD2\tAT\t3</pre></td></tr><td style='border-right:1px solid red;'>4</td><td style='text-align:left;'><pre>U_2_\t90.000000\t0.000000\t0.000000\t90.000000\tAT\t1\tIF\t3T\t2T</pre></td></tr><td style='border-right:1px solid red;'>5</td><td style='text-align:left;'><pre>SIGZ\tAT\t3\tIF\t2T</pre></td></tr><td style='border-right:1px solid red;'>6</td><td style='text-align:left;'><pre>U_2_\t90.000000\t90.000000\t90.000000\t90.000000\tAT\t0\tIF\t2F\t1F</pre></td></tr><td style='border-right:1px solid red;'>7</td><td style='text-align:left;'><pre>U_2_\t90.000000\t90.000000\t90.000000\t90.000000\tAT\t0\tIF\t2F\t1T</pre></td></tr><td style='border-right:1px solid red;'>8</td><td style='text-align:left;'><pre>U_2_\t90.000000\t90.000000\t90.000000\t90.000000\tAT\t0\tIF\t2T\t1F</pre></td></tr><td style='border-right:1px solid red;'>9</td><td style='text-align:left;'><pre>U_2_\t90.000000\t90.000000\t90.000000\t90.000000\tAT\t0\tIF\t2T\t1T</pre></td></tr></table>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<table style='font-family:monospace'><tr><td style='border-right:1px solid red;'>1</td><td style='text-align:left;'><pre>|   U   |   |</pre></td></tr><td style='border-right:1px solid red;'>2</td><td style='text-align:left;'><pre>|   O---U   |</pre></td></tr><td style='border-right:1px solid red;'>3</td><td style='text-align:left;'><pre>H   |   |   |</pre></td></tr><td style='border-right:1px solid red;'>4</td><td style='text-align:left;'><pre>@---@---Uz  |</pre></td></tr><td style='border-right:1px solid red;'>5</td><td style='text-align:left;'><pre>Z---@   |   |</pre></td></tr><td style='border-right:1px solid red;'>6</td><td style='text-align:left;'><pre>|   O---O---U</pre></td></tr><td style='border-right:1px solid red;'>7</td><td style='text-align:left;'><pre>|   O---@---U</pre></td></tr><td style='border-right:1px solid red;'>8</td><td style='text-align:left;'><pre>|   @---O---U</pre></td></tr><td style='border-right:1px solid red;'>9</td><td style='text-align:left;'><pre>|   @---@---U</pre></td></tr></table>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "for deriv_direc, dpart_name, has_neg_polarity in \\\n",
    "        [(0, 'single', None), (3, 's', True)]:\n",
    "    wr = StairsDerivCkt_writer(deriv_gate_str,\n",
    "                               has_neg_polarity,\n",
    "                               deriv_direc,\n",
    "                               dpart_name,\n",
    "                               gate_str_to_rads_list,\n",
    "                               file_prefix, emb)\n",
    "    wr.close_files()\n",
    "    print(\"%%%%%%%%%%%%%%%%%%%%%%%%%%\")\n",
    "    wr.print_eng_file(jup=True)\n",
    "    wr.print_pic_file(jup=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# class StairsDeriv_native"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "from qubiter.adv_applications.StairsDeriv_native import *"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "    This class is a child of StairsDeriv. Its main purpose is to override\n",
      "    the method get_mean_val() of its abstract parent class StairsDeriv. In\n",
      "    this class, the simulation necessary to evaluate the output of\n",
      "    get_mean_val() is done by native, Qubiter simulators.\n",
      "\n",
      "    Attributes\n",
      "    ----------\n",
      "\n",
      "    \n"
     ]
    }
   ],
   "source": [
    "# print docstring of the class\n",
    "print(StairsDeriv_native.__doc__)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "OrderedDict([('prior', ['#50', '#51', '#52', '#53']),\n",
      "             ('2F', ['#500', '#501', '#502', '#503']),\n",
      "             ('2T', ['#510', '#511', '#512', '#513']),\n",
      "             ('2F1_', ['#5050', '#5051', '#5052', '#5053']),\n",
      "             ('2T1_', ['#5150', '#5151', '#5152', '#5153'])])\n"
     ]
    }
   ],
   "source": [
    "num_qbits = 4\n",
    "parent_num_qbits = num_qbits - 1  # one bit for ancilla\n",
    "\n",
    "# u2_bit_to_higher_bits = None\n",
    "u2_bit_to_higher_bits = {0: [2], 1: [2], 2: []}\n",
    "gate_str_to_rads_list = StairsCkt_writer.\\\n",
    "    get_gate_str_to_rads_list(parent_num_qbits,\n",
    "        '#int', rads_const=np.pi/2,\n",
    "        u2_bit_to_higher_bits=u2_bit_to_higher_bits)\n",
    "pp.pprint(gate_str_to_rads_list)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2T\n"
     ]
    }
   ],
   "source": [
    "deriv_gate_str = list(gate_str_to_rads_list.keys())[2]\n",
    "print(deriv_gate_str)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [],
   "source": [
    "file_prefix = 'stairs_deriv_native_test'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The Hamiltonian `hamil` is entered as an object of class `QubitOperator`\n",
    "of the open source library `OpenFermion`. The class \n",
    "constructor simplifies the input. Once simplified, `hamil` must be a \n",
    "linear combination with real coefficients of \"pauli strings\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.4 [Y0 Y1] +\n",
      "0.7 [X1 Y2]\n"
     ]
    }
   ],
   "source": [
    "hamil = QubitOperator('X1 Y0 X1 Y1', .4) +\\\n",
    "    QubitOperator('Y2 X1', .7)\n",
    "print(hamil)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Creating an object `der` of `StairsCkt_writer` doesn't do the whole trick. You\n",
    "then need to call `der.get_mean_val(var_num_to_rads)` to get a list of the 4 partial derivatives wrt\n",
    "the 4 parameters of the U(2) transformation for the gate called `deriv_gate_str`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "partials_list= [-0.21348209139938928, 0.17099652309690933, 0.21797739856238524, 0.20955227323123532]\n"
     ]
    }
   ],
   "source": [
    "der = StairsDeriv_native(deriv_gate_str,\n",
    "                         gate_str_to_rads_list, file_prefix,\n",
    "                         parent_num_qbits, hamil)\n",
    "\n",
    "var_num_to_rads = StairsCkt_writer.get_var_num_to_rads(\n",
    "    gate_str_to_rads_list, 'const', rads_const=np.pi/2)\n",
    "\n",
    "partials_list = der.get_mean_val(var_num_to_rads)\n",
    "print('partials_list=', partials_list)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# class StairsAllDeriv_native"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [],
   "source": [
    "from qubiter.adv_applications.StairsAllDeriv_native import *"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "    This class is a child of StairsDeriv_native. For the parent class,\n",
      "    the get_mean_val() method returns a list of 4 partial derivatives\n",
      "    belonging to a particular gate string (a gate_str is a key in\n",
      "    gate_str_to_rads_list). For this class, get_mean_val() returns an\n",
      "    ordered dictionary mapping each gate_str to its 4 partials.\n",
      "\n",
      "    Attributes\n",
      "    ----------\n",
      "    deriv_gate_str : str\n",
      "\n",
      "    \n"
     ]
    }
   ],
   "source": [
    "# print docstring of the class\n",
    "print(StairsAllDeriv_native.__doc__)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "OrderedDict([('prior', ['#50', '#51', '#52', '#53']),\n",
      "             ('2F', ['#500', '#501', '#502', '#503']),\n",
      "             ('2T', ['#510', '#511', '#512', '#513']),\n",
      "             ('2F1_', ['#5050', '#5051', '#5052', '#5053']),\n",
      "             ('2T1_', ['#5150', '#5151', '#5152', '#5153'])])\n",
      "OrderedDict([('prior',\n",
      "              [-2.2204460492503132e-17,\n",
      "               0.109872851097167,\n",
      "               0.109872851097167,\n",
      "               0.06330213907425795]),\n",
      "             ('2F',\n",
      "              [-0.315426943689481,\n",
      "               0.19253599609389338,\n",
      "               0.15844289259167715,\n",
      "               0.05719217691065069]),\n",
      "             ('2T',\n",
      "              [-0.310778637864654,\n",
      "               0.0241105245882962,\n",
      "               0.01984117949460967,\n",
      "               0.007161951092978829]),\n",
      "             ('2F1_',\n",
      "              [-0.13581869634833252,\n",
      "               0.3904738483600582,\n",
      "               0.5194610247254097,\n",
      "               0.577318576543139]),\n",
      "             ('2T1_',\n",
      "              [-0.28828700434347215,\n",
      "               0.048897502352654526,\n",
      "               0.0650500585002077,\n",
      "               0.07229533187256816])])\n"
     ]
    }
   ],
   "source": [
    "num_qbits = 4\n",
    "parent_num_qbits = num_qbits - 1  # one bit for ancilla\n",
    "\n",
    "# u2_bit_to_higher_bits = None\n",
    "u2_bit_to_higher_bits = {0: [2], 1: [2], 2: []}\n",
    "gate_str_to_rads_list = StairsCkt_writer.\\\n",
    "    get_gate_str_to_rads_list(parent_num_qbits,\n",
    "        '#int', rads_const=np.pi/2,\n",
    "        u2_bit_to_higher_bits=u2_bit_to_higher_bits)\n",
    "pp.pprint(gate_str_to_rads_list)\n",
    "\n",
    "file_prefix = 'stairs_all_deriv_native_test'\n",
    "\n",
    "hamil = QubitOperator('Y0 X1', .4) +\\\n",
    "    QubitOperator('X0', .7)\n",
    "\n",
    "der = StairsAllDeriv_native(gate_str_to_rads_list, file_prefix,\n",
    "                         parent_num_qbits, hamil)\n",
    "\n",
    "var_num_to_rads = StairsCkt_writer.get_var_num_to_rads(\n",
    "    gate_str_to_rads_list, 'const', rads_const=np.pi/2)\n",
    "\n",
    "gate_str_to_partials_list = der.get_mean_val(var_num_to_rads)\n",
    "pp.pprint(gate_str_to_partials_list)\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.6"
  },
  "toc": {
   "colors": {
    "hover_highlight": "#DAA520",
    "running_highlight": "#FF0000",
    "selected_highlight": "#FFD700"
   },
   "moveMenuLeft": true,
   "nav_menu": {
    "height": "84px",
    "width": "252px"
   },
   "navigate_menu": true,
   "number_sections": true,
   "sideBar": true,
   "threshold": 4,
   "toc_cell": false,
   "toc_section_display": "block",
   "toc_window_display": false,
   "widenNotebook": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
